#region License
// Product:
// MCM (Message-Command-Message)
// mcm-framework.net
// 
// Author:
// Pawel Idzikowski
// Cologne, Germany
// idzikowski@polenter.com
// 
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the author(s) be held liable for any damages arising from
// the use of this software.
// 
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 
//     1. The origin of this software must not be misrepresented; you must not
//       claim that you wrote the original software. If you use this software
//       in a product, an acknowledgment in the product documentation would be
//       appreciated but is not required.
// 
//     2. Altered source versions must be plainly marked as such, and must not
//       be misrepresented as being the original software.
// 
//     3. This notice may not be removed or altered from any source distribution.
// 
#endregion
using System;
using System.Reflection;

namespace Polenter.Mcm
{
    /// <summary>
    /// Creates message channels using reflection
    /// </summary>
    internal class MessageChannelFactory
    {
        public MessageChannelFactory()
        {
        }

        public MessageChannelCollection CreateMessageChannels(object componentContainer)
        {
            if (componentContainer == null) throw new ArgumentNullException("componentContainer");
            var messages = new MessageChannelCollection();
            registerFields(componentContainer, messages);
            registerProperties(componentContainer, messages);
            return messages;
        }

        private void registerFields(object componentContainer, MessageChannelCollection messages)
        {
            var containerType = componentContainer.GetType();
            // Get all properties
            FieldInfo[] fields =
                containerType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic |
                                        BindingFlags.DeclaredOnly);
            foreach (FieldInfo fieldInfo in fields)
            {
                if (fieldInfo.FieldType == typeof(MessageChannel))
                {
                    // create the channel
                    var messageChannel = (MessageChannel)fieldInfo.GetValue(componentContainer);

                    if (messageChannel == null)
                    {
                        continue;
                    }

                    // adding the channel to channels
                    if (!messages.Contains(messageChannel.Name))
                    {
                        messages.Add(messageChannel);
                    }
                }
            }
        }

        private void registerProperties(object componentContainer, MessageChannelCollection messages)
        {
            var containerType = componentContainer.GetType();
            // Get all properties
            PropertyInfo[] properties =
                containerType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic |
                                            BindingFlags.DeclaredOnly);
            foreach (PropertyInfo propertyInfo in properties)
            {
                if (propertyInfo.PropertyType == typeof(MessageChannel))
                {
                    if (!propertyInfo.CanRead)
                    {
                        // such a property is of no use
                        continue;
                    }

                    // create the channel
                    var messageChannel = (MessageChannel)propertyInfo.GetValue(componentContainer, null);

                    if (messageChannel == null)
                    {
                        continue;
                    }

                    // adding the channel to channels
                    if (!messages.Contains(messageChannel.Name))
                    {
                        messages.Add(messageChannel);
                    }
                }
            }
        }        
    }
}